package monitor

import (
	consts2 "MoSkeleton/config/consts"
	"MoSkeleton/framework/core/consts"
	"MoSkeleton/framework/core/dto/response"
	"MoSkeleton/framework/core/motypes"
	"MoSkeleton/framework/mo_gin"
	"MoSkeleton/framework/mo_gin/middleware"
	"MoSkeleton/framework/utils"
	utils2 "MoSkeleton/global/utils"
	monitor2 "MoSkeleton/models/monitor"
	"MoSkeleton/services/base"
	"MoSkeleton/web/pogos/request/monitor"
	monitor3 "MoSkeleton/web/pogos/respose/monitor"
	"bytes"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"strconv"
)

type SysOperLogService struct {
	base.BaseService
}

func (s *SysOperLogService) AddOperLog(c *gin.Context, body *[]byte, writer *middleware.ResponseBodyWriter) {
	var operLog4GinReq *monitor.OperLog4GinReq
	utils.GinGet(c, consts.GinContext_OperLog_key, &operLog4GinReq)
	if operLog4GinReq == nil {
		mo_gin.MoLogger.Debug("未找到操作日志对象")
		return
	}
	operLog := &monitor2.SysOperLog{}
	if err := operLog4GinReq.CopyTo(operLog); err != nil {
		return
	}
	operLog.RequestMethod = c.Request.Method
	operLog.OperIp = utils.GetRequestIp(c)
	operLog.OperName = utils.GetUsernameFromGin(c)
	operLog.OperUrl = c.Request.URL.Path
	operLog.Status = c.Writer.Status()
	onlineUserResp := utils2.GetOnlineUserFromGin(c)
	if onlineUserResp != nil {
		operLog.DeptName = onlineUserResp.User.Dept.DeptName
	}
	if operLog4GinReq.SaveRequestData {
		operLog.OperParam = string(*body)
		if len(operLog.OperParam) > 2000 {
			operLog.OperParam = utils.SubString(operLog.OperParam, 0, 2000)
		}
	}
	if operLog4GinReq.SaveResponseData {
		operLog.JsonResult = writer.Body.String()
		if len(operLog.JsonResult) > 2000 {
			operLog.JsonResult = utils.SubString(operLog.JsonResult, 0, 2000)
		}
	}
	go func() {
		operLog.OperTime = motypes.NowTime()
		result := s.GetDb(nil).Create(operLog)
		if result.Error != nil {
			mo_gin.MoLogger.Error("操作日志写入失败", zap.Error(result.Error))
		}
	}()
}

func (o *SysOperLogService) GetOperLogPage(operLogReq *monitor.SysOperLogPageReq) (*response.PageResult, error) {
	db := o.getOperLogVo(operLogReq)
	itemsResp := make([]*monitor3.SysOperLogItemResp, 0)
	var total int64
	if err := db.Count(&total).Offset(operLogReq.Offset()).Limit(operLogReq.Limit()).Find(&itemsResp).Error; err != nil {
		return nil, err
	}
	pageResult := response.NewFromPageInfo(&operLogReq.PageInfo)
	pageResult.Rows = &itemsResp
	pageResult.Total = total
	return pageResult, nil
}

func (o *SysOperLogService) DeleteOperLogs(ids *[]uint64) (bool, error) {
	result := o.GetDb(nil).Where("id in ?", *ids).Delete(&monitor2.SysOperLog{}, ids)
	return result.RowsAffected > 0, result.Error
}

func (s *SysOperLogService) CleanTable() error {
	db := s.GetDb(nil)
	if db.Dialector.Name() == consts2.Gorm_DBType_Sqlite3 {
		return db.Exec("DELETE FROM sys_oper_log").Error
	}
	return db.Exec("truncate table sys_oper_log").Error
}

func (o *SysOperLogService) ExportOperLogs(operLogReq *monitor.SysOperLogPageReq) (*bytes.Buffer, error) {
	db := o.getOperLogVo(operLogReq)
	itemsResp := make([]*monitor3.ExportOperLogItemResp, 0)
	err := db.Find(&itemsResp).Error
	if err != nil {
		return nil, err
	}
	titles := []string{"编号", "操作模块", "业务类型", "请求方式", "操作类别", "操作人员", "部门名称", "请求地址", "请求参数", "返回参数", "状态", "操作时间", "消耗时间"}
	exportFile := utils.NewExportExcelFile()
	return exportFile.Export(consts2.Excel_Sheet_OperLog, &titles, func(row, col int, colTitle string) (value any, ok bool, err error) {
		if len(itemsResp) <= row {
			return "", false, nil
		}
		itemResp := itemsResp[row]
		switch col {
		case 0:
			return "'" + strconv.FormatUint(itemResp.ID, 10), true, nil
		case 1:
			return itemResp.Title, true, nil
		case 2:
			switch consts2.BusinessType(itemResp.BusinessType) {
			case consts2.BusinessType_OTHER:
				return "其他", true, nil
			case consts2.BusinessType_INSERT:
				return "新增", true, nil
			case consts2.BusinessType_UPDATE:
				return "修改", true, nil
			case consts2.BusinessType_DELETE:
				return "删除", true, nil
			case consts2.BusinessType_GRANT:
				return "授权", true, nil
			case consts2.BusinessType_EXPORT:
				return "导出", true, nil
			case consts2.BusinessType_IMPORT:
				return "导入", true, nil
			case consts2.BusinessType_FORCE:
				return "强退", true, nil
			case consts2.BusinessType_GENCODE:
				return "生成代码", true, nil
			case consts2.BusinessType_CLEAN:
				return "清空", true, nil
			default:
				return "其他", false, nil
			}
		case 3:
			return itemResp.RequestMethod, true, nil
		case 4:
			if consts2.OperatorType(itemResp.OperatorType) == consts2.OperatorType_MOBILE {
				return "移动端", true, nil
			} else if consts2.OperatorType(itemResp.OperatorType) == consts2.OperatorType_MANAGE {
				return "后台管理", true, nil
			} else {
				return "其他", true, nil
			}
		case 5:
			return itemResp.OperName, true, nil
		case 6:
			return itemResp.DeptName, true, nil
		case 7:
			return itemResp.OperIp, true, nil
		case 8:
			return itemResp.OperParam, true, nil
		case 9:
			return itemResp.JsonResult, true, nil
		case 10:
			if itemResp.Status == 0 {
				return "成功", true, nil
			} else {
				return "失败", true, nil
			}
		case 11:
			if itemResp.OperTime != nil {
				return itemResp.OperTime.Format("2006-01-02 15:04:05"), true, nil
			} else {
				return "", true, nil
			}
		case 12:
			return itemResp.CostTime, true, nil
		}
		return "", false, nil
	})
}

func (o *SysOperLogService) getOperLogVo(operLogReq *monitor.SysOperLogPageReq) *gorm.DB {

	db := o.GetDb(nil).Select("id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time").
		Model(&monitor2.SysOperLog{})
	if operLogReq.OperIp != "" {
		db = db.Where("oper_ip like ?", "%"+operLogReq.OperIp+"%")
	}
	if operLogReq.Title != "" {
		db = db.Where("title like ?", "%"+operLogReq.Title+"%")
	}
	if operLogReq.BusinessType != nil {
		db = db.Where("business_type = ?", *operLogReq.BusinessType)
	}
	if len(operLogReq.BusinessTypes) > 0 {
		db = db.Where("business_type in ?", operLogReq.BusinessTypes)
	}
	if operLogReq.Status != nil {
		db = db.Where("status = ?", *operLogReq.Status)
	}
	if operLogReq.OperName != "" {
		db = db.Where("oper_name like ?", "%"+operLogReq.OperName+"%")
	}
	beginTime, ok := operLogReq.GetParamValue("beginTime")
	if ok && beginTime != "" {
		if beginTime = operLogReq.FormatBeginTime(beginTime); beginTime != "" {
			db.Where("oper_time >= ?", beginTime)
		}
	}
	endTime, ok := operLogReq.GetParamValue("endTime")
	if ok && endTime != "" {
		if endTime = operLogReq.FormatEndTime(endTime); endTime != "" {
			db.Where("oper_time <= ?", endTime)
		}
	}
	db.Order("id desc")
	return db
}
